package com.wewoo.log4e.core.aop;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ReflectUtil;
import com.alibaba.fastjson.JSON;
import com.wewoo.log4e.core.LogTemplate;
import com.wewoo.log4e.core.annotation.Log4e;
import com.wewoo.log4e.core.context.Log4eHolder;
import com.wewoo.log4e.core.entity.OperationLog;
import com.wewoo.log4e.core.enums.HandleType;
import com.wewoo.log4e.core.spring.boot.autoconfigure.Log4eProperties;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Aspect
@RequiredArgsConstructor
public class LogAspect {
    private final LogTemplate logTemplate;
    private final Log4eProperties log4eProperties;
    private final Log4eHolder log4eHolder;

    @Pointcut("@annotation(com.wewoo.log4e.core.annotation.Log4e)")
    public void operationLog() {
    }

    @Around("operationLog()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object res = null;
        long time = System.currentTimeMillis();
        try {
            res = joinPoint.proceed();
            time = System.currentTimeMillis() - time;
            return res;
        } finally {
            try {
                //方法执行完成后增加日志
                addOperationLog(joinPoint, res, time);
            } catch (Exception e) {
                System.out.println("LogAspect 操作失败：" + e.getMessage());
                e.printStackTrace();
            }
        }
    }

    private void addOperationLog(JoinPoint joinPoint, Object res, long time) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        OperationLog operationLog = new OperationLog();
        operationLog.setRelateUnique("1111");
        operationLog.setRunTime(time);
        operationLog.setReturnValue(null!=res?JSON.toJSONString(res):"empty");
        operationLog.setId(IdUtil.getSnowflake().nextId());
        operationLog.setArgs(JSON.toJSONString(joinPoint.getArgs()));
        operationLog.setCreateTime(new Date());
        operationLog.setMethod(signature.getDeclaringTypeName() + "." + signature.getName());
        operationLog.setUserId(log4eHolder.getLoginUserId());
        operationLog.setUserName(log4eHolder.getLoginUserName());
        operationLog.setCreateTime(new Date());
        Log4e annotation = signature.getMethod().getAnnotation(Log4e.class);
        if (annotation != null) {
            operationLog.setLevel(annotation.level());
            if (annotation.handleType().equals(HandleType.FRONT)) {
                operationLog.setDescripition(getDetail(((MethodSignature) joinPoint.getSignature()).getParameterNames(), joinPoint.getArgs(), annotation));
            } else {
                operationLog.setDescripition(getDetailAfter(res, annotation));
            }
            operationLog.setOperationType(annotation.operationType().getValue());
            operationLog.setOperationUnit(annotation.operationUnit().getValue());
            operationLog.setHandleType(annotation.handleType().getValue());
        }
        //TODO 这里保存日志
        System.out.println("记录日志:" + operationLog.toString());
//        operationLogService.insert(operationLog);
        logTemplate.writeLog(operationLog,annotation.executor());
    }

    /**
     * 对当前登录用户和占位符处理
     *
     * @param argNames   方法参数名称数组
     * @param args       方法参数数组
     * @param annotation 注解信息
     * @return 返回处理后的描述
     */
    private String getDetail(String[] argNames, Object[] args, Log4e annotation) {
        Map<Object, Object> map = new HashMap<>(4);
        for (int i = 0; i < argNames.length; i++) {
            map.put(argNames[i], args[i]);
        }
        String detail = annotation.detail();
        try {
            detail = "" + log4eHolder.getLoginUserName() + annotation.detail();
            for (Map.Entry<Object, Object> entry : map.entrySet()) {
                Object k = entry.getKey();
                Object v = entry.getValue();
                if (v.getClass().getPackage().getName().startsWith("com.wewoo.")) {
                    Field field = ReflectUtil.getField(v.getClass(), "operateParam");
                    field.setAccessible(true);
                    String[] operateParam = (String[]) field.get(v);
                    if (null != operateParam) {
                        for (int i = 0; i < operateParam.length; i++) {
                            String pname = "param" + (i + 1);
                            detail = detail.replace("{{" + pname + "}}", JSON.toJSONString(operateParam[i]));
                        }
                    }
                } else {
                    detail = detail.replace("{{" + k + "}}", JSON.toJSONString(v));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return detail;
    }

    private String getDetailAfter(Object res, Log4e annotation) {
        Map<Object, Object> map = new HashMap<>(4);
        map.put(res.getClass().getName(), res);
        String detail = annotation.detail();
        try {
            detail = "" + log4eHolder.getLoginUserName() + annotation.detail();
            for (Map.Entry<Object, Object> entry : map.entrySet()) {
                Object k = entry.getKey();
                Object v = entry.getValue();
                if (v.getClass().getPackage().getName().startsWith("com.wewoo.")) {
                    Field field = ReflectUtil.getField(v.getClass(), "operateParam");
                    field.setAccessible(true);
                    String[] operateParam = (String[]) field.get(v);
                    if (null != operateParam) {
                        for (int i = 0; i < operateParam.length; i++) {
                            String pname = "param" + (i + 1);
                            detail = detail.replace("{{" + pname + "}}", JSON.toJSONString(operateParam[i]));
                        }
                    }
                } else {
                    detail = detail.replace("{{" + k + "}}", JSON.toJSONString(v));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return detail;
    }

    @Before("operationLog()")
    public void doBeforeAdvice(JoinPoint joinPoint) {
    }

    /**
     * 处理完请求，返回内容
     *
     * @param ret
     */
    @AfterReturning(returning = "ret", pointcut = "operationLog()")
    public void doAfterReturning(Object ret) {
    }

    /**
     * 后置异常通知
     */
    @AfterThrowing("operationLog()")
    public void throwss(JoinPoint jp) {
    }


    /**
     * 后置最终通知,final增强，不管是抛出异常或者正常退出都会执行
     */
    @After("operationLog()")
    public void after(JoinPoint jp) {
    }


}
